package org.xxpay.service.impl;

import com.alibaba.fastjson.JSONObject;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.xxpay.core.common.constant.MchConstant;
import org.xxpay.core.common.constant.PayConstant;
import org.xxpay.core.common.constant.RetEnum;
import org.xxpay.core.common.exception.ServiceException;
import org.xxpay.core.common.util.DateUtil;
import org.xxpay.core.common.util.MyBase64;
import org.xxpay.core.common.util.MyLog;
import org.xxpay.core.common.util.XXPayUtil;
import org.xxpay.core.common.vo.OrderCostFeeVO;
import org.xxpay.core.entity.PayInterfaceTypeTemplate;
import org.xxpay.core.entity.PayOrder;
import org.xxpay.core.entity.PayOrderExample;
import org.xxpay.core.entity.ResendRecord;
import org.xxpay.core.service.*;
import org.xxpay.service.common.annotation.DataSourceSwitch;
import org.xxpay.service.common.enumm.DataSourceType;
import org.xxpay.service.dao.mapper.PayInterfaceTypeTemplateMapper;
import org.xxpay.service.dao.mapper.PayOrderMapper;

import java.util.*;

/**
 * @author: dingzhiwei
 * @date: 17/9/8
 * @description:
 */
@Service
public class PayOrderServiceImpl implements IPayOrderService {

    @Autowired
    private PayOrderMapper payOrderMapper;

    @Autowired
    private IMchAccountService mchAccountService;

    @Autowired
    private IAgentInfoService agentInfoService;

    @Autowired
    private PayInterfaceTypeTemplateMapper payInterfaceTypeTemplateMapper;

    @Autowired
    private IResendRecordService resendRecordService;

    @Autowired
    private IPayAlizftMchInfoService payAlizftMchInfoService;

    private static final MyLog _log = MyLog.getLog(PayOrderServiceImpl.class);

    @Override
    @Transactional(transactionManager = "transactionManager", rollbackFor = Exception.class)
    public int createPayOrder(PayOrder payOrder) {
        return payOrderMapper.insertSelective(payOrder);
    }

    public PayOrder selectPayOrder(String payOrderId) {
        return payOrderMapper.selectByPrimaryKey(payOrderId);
    }

    public PayOrder selectByMchIdAndPayOrderId(Long mchId, String payOrderId) {
        PayOrderExample example = new PayOrderExample();
        PayOrderExample.Criteria criteria = example.createCriteria();
        criteria.andMchIdEqualTo(mchId);
        criteria.andPayOrderIdEqualTo(payOrderId);
        List<PayOrder> payOrderList = payOrderMapper.selectByExample(example);
        return org.springframework.util.CollectionUtils.isEmpty(payOrderList) ? null : payOrderList.get(0);
    }

    public PayOrder selectByMchIdAndMchOrderNo(Long mchId, String mchOrderNo) {
        PayOrderExample example = new PayOrderExample();
        PayOrderExample.Criteria criteria = example.createCriteria();
        criteria.andMchIdEqualTo(mchId);
        criteria.andMchOrderNoEqualTo(mchOrderNo);
        List<PayOrder> payOrderList = payOrderMapper.selectByExample(example);
        return org.springframework.util.CollectionUtils.isEmpty(payOrderList) ? null : payOrderList.get(0);
    }

    public int updateStatus4Ing(String payOrderId, String channelOrderNo) {
        return updateStatus4Ing(payOrderId, channelOrderNo, null);
    }

    public int updateStatus4Ing(String payOrderId, String channelOrderNo, String channelAttach) {
        PayOrder payOrder = new PayOrder();
        payOrder.setStatus(PayConstant.PAY_STATUS_PAYING);
        if(channelOrderNo != null) payOrder.setChannelOrderNo(channelOrderNo);
        if(channelAttach != null) payOrder.setChannelAttach(channelAttach);
        PayOrderExample example = new PayOrderExample();
        PayOrderExample.Criteria criteria = example.createCriteria();
        criteria.andPayOrderIdEqualTo(payOrderId);
        criteria.andStatusEqualTo(PayConstant.PAY_STATUS_INIT);
        return payOrderMapper.updateByExampleSelective(payOrder, example);
    }

    @Transactional(transactionManager = "transactionManager", rollbackFor = Exception.class)
    public int updateStatus4Success(String payOrderId) {
        return updateStatus4Success(payOrderId, null);
    }

    @Transactional(transactionManager = "transactionManager", rollbackFor = Exception.class)
    public int updateStatus4Success(String payOrderId, String channelOrderNo) {
        PayOrder payOrder = new PayOrder();
        payOrder.setPayOrderId(payOrderId);
        payOrder.setStatus(PayConstant.PAY_STATUS_SUCCESS);
        payOrder.setPaySuccTime(new Date());
        if(StringUtils.isNotBlank(channelOrderNo)) payOrder.setChannelOrderNo(channelOrderNo);
        PayOrderExample example = new PayOrderExample();
        PayOrderExample.Criteria criteria = example.createCriteria();
        criteria.andPayOrderIdEqualTo(payOrderId);
        criteria.andStatusEqualTo(PayConstant.PAY_STATUS_PAYING);
        return updateSuccess4Transactional(payOrder, example);
    }

    @Transactional(transactionManager = "transactionManager", rollbackFor = Exception.class)
    public int updateStatus4Success2(String payOrderId) {
        PayOrder payOrder = new PayOrder();
        payOrder.setPayOrderId(payOrderId);
        payOrder.setStatus(PayConstant.PAY_STATUS_SUCCESS);
        payOrder.setPaySuccTime(new Date());
        PayOrderExample example = new PayOrderExample();
        PayOrderExample.Criteria criteria = example.createCriteria();
        criteria.andPayOrderIdEqualTo(payOrderId);
        criteria.andStatusEqualTo(PayConstant.PAY_STATUS_CLOSED);
        return updateSuccess4Transactional(payOrder, example);
    }

    @Transactional(transactionManager = "transactionManager", rollbackFor = Exception.class)
    public int updateStatus4Success(String payOrderId, String channelOrderNo, String channelAttach) {
        PayOrder payOrder = new PayOrder();
        payOrder.setPayOrderId(payOrderId);
        payOrder.setStatus(PayConstant.PAY_STATUS_SUCCESS);
        payOrder.setPaySuccTime(new Date());
        if(StringUtils.isNotBlank(channelOrderNo)) payOrder.setChannelOrderNo(channelOrderNo);
        if(StringUtils.isNotBlank(channelAttach)) payOrder.setChannelAttach(channelAttach);
        PayOrderExample example = new PayOrderExample();
        PayOrderExample.Criteria criteria = example.createCriteria();
        criteria.andPayOrderIdEqualTo(payOrderId);
        criteria.andStatusEqualTo(PayConstant.PAY_STATUS_PAYING);
        return updateSuccess4Transactional(payOrder, example);
    }

    @Transactional(transactionManager = "transactionManager", rollbackFor = Exception.class)
    int updateSuccess4Transactional(PayOrder payOrder, PayOrderExample example) {
        int count = payOrderMapper.updateByExampleSelective(payOrder, example);
        // 更新成功且为平台账户,增加商户资金账户流水记录
        payOrder = selectPayOrder(payOrder.getPayOrderId());

        if(count > 0) { //订单更新成功, 更新统计金额
            PayInterfaceTypeTemplate updateRecord = new PayInterfaceTypeTemplate();
            updateRecord.setTotalPaymentAmount(payOrder.getAmount());
            updateRecord.setTotalIncomeAmount(payOrder.getAmount() - payOrder.getChannelCost());
            updateRecord.setPsVal("passageAccountId", payOrder.getPassageAccountId()); //根据payPassageId更新
            payInterfaceTypeTemplateMapper.addTotalAmount(updateRecord);

            if(StringUtils.isNotEmpty(payOrder.getChannelSMId())){ //存在smID 累加金额
                payAlizftMchInfoService.updateDayAmount(payOrder.getChannelSMId(), payOrder.getPassageAccountId(), payOrder.getAmount());
            }

        }

        if(count == 1 && payOrder.getMchType() == MchConstant.MCH_TYPE_PLATFORM) {
            mchAccountService.changeBalanceAndInsertHistory(payOrder, false, null);

        }
        return count;
    }

    public int updateStatus4Complete(String payOrderId) {
        PayOrder payOrder = new PayOrder();
        payOrder.setPayOrderId(payOrderId);
        payOrder.setStatus(PayConstant.PAY_STATUS_COMPLETE);
        PayOrderExample example = new PayOrderExample();
        PayOrderExample.Criteria criteria = example.createCriteria();
        criteria.andPayOrderIdEqualTo(payOrderId);
        criteria.andStatusEqualTo(PayConstant.PAY_STATUS_SUCCESS);
        return payOrderMapper.updateByExampleSelective(payOrder, example);
    }

    // 更新为失败
    @Override
    @Transactional(transactionManager = "transactionManager", rollbackFor = Exception.class)
    public int updateStatus4Fail(String payOrderId) {
        PayOrder payOrder = new PayOrder();
        payOrder.setPayOrderId(payOrderId);
        payOrder.setStatus(PayConstant.PAY_STATUS_FAILED);
        PayOrderExample example = new PayOrderExample();
        PayOrderExample.Criteria criteria = example.createCriteria();
        criteria.andPayOrderIdEqualTo(payOrderId);
        criteria.andStatusEqualTo(PayConstant.PAY_STATUS_PAYING);
        int updateRow =  payOrderMapper.updateByExampleSelective(payOrder, example);

        if(updateRow <= 0 ) return 0; //支付失败

        PayOrder dbPayOrder = payOrderMapper.selectByPrimaryKey(payOrderId);

        return 1;
    }

    public int updateNotify(String payOrderId, byte count) {
        PayOrder newPayOrder = new PayOrder();
        newPayOrder.setPayOrderId(payOrderId);
        return payOrderMapper.updateByPrimaryKeySelective(newPayOrder);
    }

    @Override
    public PayOrder find(PayOrder payOrder) {
        PayOrderExample example = new PayOrderExample();
        PayOrderExample.Criteria criteria = example.createCriteria();
        setCriteria(criteria, payOrder);
        List<PayOrder> payOrderList = payOrderMapper.selectByExample(example);
        if(CollectionUtils.isEmpty(payOrderList)) return null;
        return payOrderList.get(0);
    }

    @Override
    public PayOrder findByPayOrderId(String payOrderId) {
        return payOrderMapper.selectByPrimaryKey(payOrderId);
    }

    @Override
    public PayOrder findByMchIdAndPayOrderId(Long mchId, String payOrderId) {
        return selectByMchIdAndPayOrderId(mchId, payOrderId);
    }

    @Override
    public PayOrder findByMchOrderNo(String mchOrderNo) {
        PayOrder payOrder = new PayOrder();
        payOrder.setMchOrderNo(mchOrderNo);
        return find(payOrder);
    }

    @Override
    public List<PayOrder> select(Long mchId, int offset, int limit, PayOrder payOrder, Date createTimeStart, Date createTimeEnd) {
        PayOrderExample example = new PayOrderExample();
        example.setOrderByClause("createTime DESC");
        example.setOffset(offset);
        example.setLimit(limit);
        PayOrderExample.Criteria criteria = example.createCriteria();
        if(mchId != null) criteria.andMchIdEqualTo(mchId);
        setCriteria(criteria, payOrder, createTimeStart, createTimeEnd);
        return payOrderMapper.selectByExample(example);
    }

    @Override
    public List<PayOrder> select(int offset, int limit, PayOrder payOrder, Date createTimeStart, Date createTimeEnd) {
        return select(null, offset, limit, payOrder, createTimeStart, createTimeEnd);
    }

    @Override
    public Integer count(Long mchId, PayOrder payOrder, Date createTimeStart, Date createTimeEnd) {
        PayOrderExample example = new PayOrderExample();
        PayOrderExample.Criteria criteria = example.createCriteria();
        if(mchId != null) criteria.andMchIdEqualTo(mchId);
        setCriteria(criteria, payOrder, createTimeStart, createTimeEnd);
        return payOrderMapper.countByExample(example);
    }

    @Override
    public Integer count(PayOrder payOrder, Date createTimeStart, Date createTimeEnd) {
        return count(null, payOrder, createTimeStart, createTimeEnd);
    }

    @Override
    public Integer count(PayOrder payOrder, List<Byte> statusList) {
        PayOrderExample example = new PayOrderExample();
        PayOrderExample.Criteria criteria = example.createCriteria();
        setCriteria(criteria, payOrder);
        if(CollectionUtils.isNotEmpty(statusList)) criteria.andStatusIn(statusList);
        return payOrderMapper.countByExample(example);
    }

    @Override
    public int updateByPayOrderId(String payOrderId, PayOrder payOrder) {
        payOrder.setPayOrderId(payOrderId);
        return payOrderMapper.updateByPrimaryKeySelective(payOrder);


    }

    @Override
    public Long sumAmount(PayOrder payOrder, List<Byte> statusList) {
        PayOrderExample example = new PayOrderExample();
        PayOrderExample.Criteria criteria = example.createCriteria();
        setCriteria(criteria, payOrder);
        if(CollectionUtils.isNotEmpty(statusList)) criteria.andStatusIn(statusList);
        return payOrderMapper.sumAmountByExample(example);
    }

    @Override
    public List<PayOrder> select(String channelMchId, String billDate, List<Byte> statusList) {
        PayOrderExample example = new PayOrderExample();
        example.setOrderByClause("createTime DESC");
        PayOrderExample.Criteria criteria = example.createCriteria();
        if(StringUtils.isNotBlank(channelMchId)) criteria.andChannelMchIdEqualTo(channelMchId);
        if(CollectionUtils.isNotEmpty(statusList)) criteria.andStatusIn(statusList);
        if(StringUtils.isNotBlank(billDate)) {
            Date beginDate = DateUtil.str2date(billDate + " 00:00:00");
            Date endDate = DateUtil.str2date(billDate + " 23:59:59");
            criteria.andCreateTimeBetween(beginDate, endDate);
        }
        return payOrderMapper.selectByExample(example);
    }

    @Override
    public List<PayOrder> selectAllBill(int offset, int limit, String billDate) {
        PayOrderExample example = new PayOrderExample();
        example.setOrderByClause("mchId ASC");
        example.setLimit(limit);
        example.setOffset(offset);
        PayOrderExample.Criteria criteria = example.createCriteria();
        List<Byte> statusList = new LinkedList<>();
        // 查询成功或处理完成
        statusList.add(PayConstant.PAY_STATUS_SUCCESS);
        statusList.add(PayConstant.PAY_STATUS_COMPLETE);
        criteria.andStatusIn(statusList);
        if(StringUtils.isNotBlank(billDate)) {
            Date beginDate = DateUtil.str2date(billDate + " 00:00:00");
            Date endDate = DateUtil.str2date(billDate + " 23:59:59");
            criteria.andCreateTimeBetween(beginDate, endDate);
        }
        return payOrderMapper.selectByExample(example);
    }

    @Override
    @DataSourceSwitch(DataSourceType.SLAVE)
    public Map count4Income(Long agentId, Long mchId, Byte productType, String createTimeStart, String createTimeEnd) {
        Map param = new HashMap<>();
        if(agentId != null) param.put("agentId", agentId);
        if(mchId != null) param.put("mchId", mchId);
        if(productType != null) param.put("productType", productType);
        if(StringUtils.isNotBlank(createTimeStart)) param.put("createTimeStart", createTimeStart);
        if(StringUtils.isNotBlank(createTimeEnd)) param.put("createTimeEnd", createTimeEnd);
        return payOrderMapper.count4Income(param);
    }

    @Override
    @DataSourceSwitch(DataSourceType.SLAVE)
    public List<Map> count4MchTop(Long agentId, Long mchId, Byte productType, String createTimeStart, String createTimeEnd) {
        Map param = new HashMap<>();
        if(agentId != null) param.put("agentId", agentId);
        if(mchId != null) param.put("mchId", mchId);
        if(productType != null && productType != -99) param.put("productType", productType);
        if(StringUtils.isNotBlank(createTimeStart)) param.put("createTimeStart", createTimeStart);
        if(StringUtils.isNotBlank(createTimeEnd)) param.put("createTimeEnd", createTimeEnd);
        return payOrderMapper.count4MchTop(param);
    }

    /*@Override
    public List<Map> count4AgentTop(String agentId, String bizType, String createTimeStart, String createTimeEnd) {
        Map param = new HashMap<>();
        if(StringUtils.isNotBlank(agentId)) param.put("agentId", agentId);
        if(StringUtils.isNotBlank(bizType)) param.put("bizType", bizType);
        if(StringUtils.isNotBlank(createTimeStart)) param.put("createTimeStart", createTimeStart);
        if(StringUtils.isNotBlank(createTimeEnd)) param.put("createTimeEnd", createTimeEnd);
        return payOrderMapper.count4AgentTop(param);
    }*/

    @Override
    public List<Map> count4Pay(String idName, String createTimeStart, String createTimeEnd) {
        Map param = new HashMap<>();
        if(StringUtils.isBlank(idName)) return null;
        param.put("idName", idName);
        if(StringUtils.isNotBlank(createTimeStart)) param.put("createTimeStart", createTimeStart);
        if(StringUtils.isNotBlank(createTimeEnd)) param.put("createTimeEnd", createTimeEnd);
        return payOrderMapper.count4Pay(param);
    }

    @Override
    @DataSourceSwitch(DataSourceType.SLAVE)
    public List<Map> count4PayProduct(String createTimeStart, String createTimeEnd) {
        Map param = new HashMap<>();
        if(StringUtils.isNotBlank(createTimeStart)) param.put("createTimeStart", createTimeStart);
        if(StringUtils.isNotBlank(createTimeEnd)) param.put("createTimeEnd", createTimeEnd);
        return payOrderMapper.count4PayProduct(param);
    }

    @Override
    public Long sumAmount4PayPassageAccount(int payPassageAccountId, Date creatTimeStart, Date createTimeEnd) {
        PayOrderExample example = new PayOrderExample();
        PayOrderExample.Criteria criteria = example.createCriteria();
        criteria.andPassageAccountIdEqualTo(payPassageAccountId);
        if(creatTimeStart != null) {
            criteria.andCreateTimeGreaterThanOrEqualTo(creatTimeStart);
        }
        if(createTimeEnd != null) {
            criteria.andCreateTimeLessThanOrEqualTo(createTimeEnd);
        }
        // 统计支付成功和处理完成的订单
        List<Byte> statusList = new LinkedList<>();
        statusList.add(PayConstant.PAY_STATUS_SUCCESS);
        statusList.add(PayConstant.PAY_STATUS_COMPLETE);
        //criteria.andStatusNotEqualTo(PayConstant.PAY_STATUS_INIT);
        return payOrderMapper.sumAmountByExample(example);
    }

    @Override
    @DataSourceSwitch(DataSourceType.SLAVE)
    public Map count4All(Long agentId, Long mchId,  Long productId, Long passageId, Long passageAccountId, String payOrderId,
                         String mchOrderNo, Byte productType, String channelMchId, String subject, Long amount, String skBank, String skName, Byte isReissue, String createTimeStart, String createTimeEnd) {
        Map param = new HashMap<>();
        if(agentId != null) param.put("agentId", agentId);
        if(mchId != null) param.put("mchId", mchId);
        if(passageId != null) param.put("passageId", passageId);
        if(passageAccountId != null) param.put("passageAccountId", passageAccountId);
        if(productId != null && productId != -99) param.put("productId", productId);
        if(StringUtils.isNotBlank(payOrderId)) param.put("payOrderId", payOrderId);
        if(StringUtils.isNotBlank(mchOrderNo)) param.put("mchOrderNo", mchOrderNo);
        if(productType != null && productType != -99) param.put("productType", productType);
        if(StringUtils.isNotBlank(channelMchId)) param.put("channelMchId", channelMchId);
        if(StringUtils.isNotBlank(subject)) param.put("subject", subject);
        if(amount != null) param.put("amount", amount);
        if (isReissue != null && isReissue != -99) param.put("isReissue", isReissue);
        if (StringUtils.isNotEmpty(skBank)) param.put("skBank", skBank);
        if (StringUtils.isNotEmpty(skName)) param.put("skName", skName);
        if(StringUtils.isNotBlank(createTimeStart)) param.put("createTimeStart", createTimeStart);
        if(StringUtils.isNotBlank(createTimeEnd)) param.put("createTimeEnd", createTimeEnd);
        return payOrderMapper.count4All(param);
    }

    @Override
    @DataSourceSwitch(DataSourceType.SLAVE)
    public Map count4Success(Long agentId, Long mchId, Long productId, Long passageId, Long passageAccountId, String payOrderId,
                             String mchOrderNo, Byte productType, String channelMchId, String subject, Long amount, String skBank, String skName, Byte isReissue, String createTimeStart, String createTimeEnd) {
        Map param = new HashMap<>();
        if(agentId != null) param.put("agentId", agentId);
        if(mchId != null) param.put("mchId", mchId);
        if(passageId != null) param.put("passageId", passageId);
        if(passageAccountId != null) param.put("passageAccountId", passageAccountId);
        if(productId != null && productId != -99) param.put("productId", productId);
        if(StringUtils.isNotBlank(payOrderId)) param.put("payOrderId", payOrderId);
        if(StringUtils.isNotBlank(mchOrderNo)) param.put("mchOrderNo", mchOrderNo);
        if(productType != null && productType != -99) param.put("productType", productType);
        if(StringUtils.isNotBlank(channelMchId)) param.put("channelMchId", channelMchId);
        if(StringUtils.isNotBlank(subject)) param.put("subject", subject);
        if(amount != null) param.put("amount", amount);
        if (isReissue != null && isReissue != -99) param.put("isReissue", isReissue);
        if (StringUtils.isNotEmpty(skBank)) param.put("skBank", skBank);
        if (StringUtils.isNotEmpty(skName)) param.put("skName", skName);
        if(StringUtils.isNotBlank(createTimeStart)) param.put("createTimeStart", createTimeStart);
        if(StringUtils.isNotBlank(createTimeEnd)) param.put("createTimeEnd", createTimeEnd);
        return payOrderMapper.count4Success(param);
    }

    @Override
    @DataSourceSwitch(DataSourceType.SLAVE)
    public Map count4Fail(Long agentId, Long mchId, Long productId, Long passageId, Long passageAccountId, String payOrderId,
                          String mchOrderNo, Byte productType, String channelMchId, String subject, Long amount, String skBank, String skName, Byte isReissue, String createTimeStart, String createTimeEnd) {
        Map param = new HashMap<>();
        if(agentId != null) param.put("agentId", agentId);
        if(mchId != null) param.put("mchId", mchId);
        if(passageId != null) param.put("passageId", passageId);
        if(passageAccountId != null) param.put("passageAccountId", passageAccountId);
        if(productId != null && productId != -99) param.put("productId", productId);
        if(StringUtils.isNotBlank(payOrderId)) param.put("payOrderId", payOrderId);
        if(StringUtils.isNotBlank(mchOrderNo)) param.put("mchOrderNo", mchOrderNo);
        if(productType != null && productType != -99) param.put("productType", productType);
        if(StringUtils.isNotBlank(channelMchId)) param.put("channelMchId", channelMchId);
        if(StringUtils.isNotBlank(subject)) param.put("subject", subject);
        if(amount != null) param.put("amount", amount);
        if (isReissue != null && isReissue != -99) param.put("isReissue", isReissue);
        if (StringUtils.isNotEmpty(skBank)) param.put("skBank", skBank);
        if (StringUtils.isNotEmpty(skName)) param.put("skName", skName);
        if(StringUtils.isNotBlank(createTimeStart)) param.put("createTimeStart", createTimeStart);
        if(StringUtils.isNotBlank(createTimeEnd)) param.put("createTimeEnd", createTimeEnd);
        return payOrderMapper.count4Fail(param);
    }

    @Override
    @DataSourceSwitch(DataSourceType.SLAVE)
    public List<Map> daySuccessRate(int offset, int limit, String createTimeStart, String createTimeEnd, Long mchId) {
        Map param = new HashMap<>();
        param.put("offset",offset);
        param.put("limit",limit);
        if(mchId != null) param.put("mchId", mchId);
        if(StringUtils.isNotBlank(createTimeStart)) param.put("createTimeStart", createTimeStart);
        if(StringUtils.isNotBlank(createTimeEnd)) param.put("createTimeEnd", createTimeEnd);
        return payOrderMapper.daySuccessRate(param);
    }

    @Override
    @DataSourceSwitch(DataSourceType.SLAVE)
    public List<Map> hourSuccessRate(int offset, int limit, String createTimeStart, String createTimeEnd, Long mchId) {
        Map param = new HashMap<>();
        param.put("offset",offset);
        param.put("limit",limit);
        if(mchId != null) param.put("mchId", mchId);
        if(StringUtils.isNotBlank(createTimeStart)) param.put("createTimeStart", createTimeStart);
        if(StringUtils.isNotBlank(createTimeEnd)) param.put("createTimeEnd", createTimeEnd);
        return payOrderMapper.hourSuccessRate(param);
    }

    @Override
    @DataSourceSwitch(DataSourceType.SLAVE)
    public Map<String, Object> countDaySuccessRate(String createTimeStart, String createTimeEnd, Long mchId) {
        Map param = new HashMap<>();
        if(mchId != null) param.put("mchId", mchId);
        if(StringUtils.isNotBlank(createTimeStart)) param.put("createTimeStart", createTimeStart);
        if(StringUtils.isNotBlank(createTimeEnd)) param.put("createTimeEnd", createTimeEnd);
        return payOrderMapper.countDaySuccessRate(param);
    }
    @Override
    @DataSourceSwitch(DataSourceType.SLAVE)
    public Map<String, Object> countHourSuccessRate(String createTimeStart, String createTimeEnd, Long mchId) {
        Map param = new HashMap<>();
        if(mchId != null) param.put("mchId", mchId);
        if(StringUtils.isNotBlank(createTimeStart)) param.put("createTimeStart", createTimeStart);
        if(StringUtils.isNotBlank(createTimeEnd)) param.put("createTimeEnd", createTimeEnd);
        return payOrderMapper.countHourSuccessRate(param);
    }

    @Override
    @DataSourceSwitch(DataSourceType.SLAVE)
    public Map dateRate(String dayStart, String dayEnd) {
        Map param = new HashMap<>();
        if(StringUtils.isNotBlank(dayStart)) param.put("createTimeStart", dayStart);
        if(StringUtils.isNotBlank(dayEnd)) param.put("createTimeEnd", dayEnd);
        return payOrderMapper.dateRate(param);
    }

    @Override
    @DataSourceSwitch(DataSourceType.SLAVE)
    public Map hourRate(String dayStart, String dayEnd) {
        Map param = new HashMap<>();
        if(StringUtils.isNotBlank(dayStart)) param.put("createTimeStart", dayStart);
        if(StringUtils.isNotBlank(dayEnd)) param.put("createTimeEnd", dayEnd);
        return payOrderMapper.hourRate(param);
    }

    @Override
    @DataSourceSwitch(DataSourceType.SLAVE)
    public Map orderDayAmount(Long mchId, String dayStart, String dayEnd) {
        Map param = new HashMap<>();
        if (mchId != null) param.put("mchId",mchId);
        if (StringUtils.isNotBlank(dayStart)) param.put("createTimeStart", dayStart);
        if (StringUtils.isNotBlank(dayEnd)) param.put("createTimeEnd", dayEnd);
        return payOrderMapper.orderDayAmount(param);
    }

    @Override
    public Long getOrderTimeLeft(String payOrderId, Long timeOut) {
        Map param = new HashMap<>();
        param.put("payOrderId", payOrderId);
        param.put("timeOut", timeOut);
        return payOrderMapper.getOrderTimeLeft(param);
    }

    @Override
    public Long getAvailableAmount(PayOrder payOrder, Long payTimeOut, Long incrRange, Long incrStep) {
        Map param = new HashMap<>();
        Long amount = payOrder.getAmount();
        param.put("channelMchId", payOrder.getChannelMchId());
        param.put("minAmount", amount);
        param.put("maxAmount", amount + incrRange);
        param.put("timeOut", payTimeOut);
        List<Map> orderList = payOrderMapper.selectAmountRange(param);
        if(CollectionUtils.isEmpty(orderList)) return amount;
        Map<String, Long> dbAmountMap = new HashMap<>();
        for(Map map : orderList) {
            dbAmountMap.put(map.get("amount").toString(), Long.parseLong(map.get("amount").toString()));
        }
        // 从amount增加到amount+amountRange,如果db中没有则返回
        for(long i=amount; i<=amount+incrRange; i=i+incrStep) {
            if(dbAmountMap.get(i+"") == null) {
                return i;
            }
        }
        return null;
    }

    @Override
    public Long getAvailableAmount4Decr(PayOrder payOrder, Long payTimeOut, Long incrRange, Long decrStep) {
        Map param = new HashMap<>();
        Long amount = payOrder.getAmount();
        param.put("channelId", payOrder.getChannelId());
        param.put("channelMchId", payOrder.getChannelMchId());
        long minAmount = Math.max(amount - incrRange, 1);
        long maxAmount = amount;
        param.put("minAmount",  minAmount);
        param.put("maxAmount", maxAmount);
        param.put("timeOut", payTimeOut);
        List<Map> orderList = payOrderMapper.selectAmountRange(param);
        if(CollectionUtils.isEmpty(orderList)) return amount;
        Map<String, Long> dbAmountMap = new HashMap<>();
        for(Map map : orderList) {
            dbAmountMap.put(map.get("amount").toString(), Long.parseLong(map.get("amount").toString()));
        }
        // 从amount增加到amount+amountRange,如果db中没有则返回(并发大时可能会有线程安全问题)
        for(long i = maxAmount; i >= minAmount; i = i-decrStep) {
            if(dbAmountMap.get(i+"") == null) {
                return i;
            }
        }
        return null;
    }

    @Override
    public PayOrder findByAmount(Long amount, String rightCardNo, Long payTimeOut) {
        Map param = new HashMap<>();
        param.put("amount", amount);
        param.put("rightCardNo", rightCardNo);
        param.put("timeOut", payTimeOut);
        List<PayOrder> orderList = payOrderMapper.selectByAmount(param);
        if(CollectionUtils.isEmpty(orderList)) return null;
        if(orderList.size() > 1) {
            _log.info("根据金额查订单有多个,不能确定唯一订单，返回空,orderList={}", orderList);
            return null;
        }
        return orderList.get(0);
    }

    @Override
    public List<PayOrder> selectDivisionOrder() {
        return payOrderMapper.selectDivisionOrder();
    }

    @Override
    public boolean hasOrderByMchOrderNo(Long mchId, String mchOrderNo) {

        PayOrderExample exa = new PayOrderExample();
        exa.createCriteria().andMchIdEqualTo(mchId).andMchOrderNoEqualTo(mchOrderNo);
        return payOrderMapper.countByExample(exa) > 0;
    }


    @Override
    @Transactional(transactionManager = "transactionManager", rollbackFor = Exception.class)
    public int updateOrderClosed(PayOrder payOrder) {

        PayOrder updateRecord = new PayOrder();
        updateRecord.setStatus(PayConstant.PAY_STATUS_CLOSED);

        // 关闭订单
        PayOrderExample updateCondition = new PayOrderExample();
        updateCondition.createCriteria().andExpireTimeLessThan(new Date())
                .andStatusIn(Arrays.asList(PayConstant.PAY_STATUS_INIT, PayConstant.PAY_STATUS_PAYING))
                .andPayOrderIdEqualTo(payOrder.getPayOrderId());

        int updateRow = payOrderMapper.updateByExampleSelective(updateRecord, updateCondition);

        // 更新失败
        if (updateRow <= 0) return 0;
        return 1;
    }

    @Override
    public List<PayOrder> selectByExample(PayOrderExample example){
        return payOrderMapper.selectByExample(example);
    }

    @Override
    @Transactional(transactionManager = "transactionManager", rollbackFor = Exception.class)
    public void updateReissue(String payOrderId, Byte isReissue) {
        PayOrder payOrder = payOrderMapper.selectByPrimaryKey(payOrderId);
        payOrder.setIsReissue(PayConstant.PAY_ISREISSUE_YES);
        payOrderMapper.updateByPrimaryKey(payOrder);
    }

    @Override
    @Transactional(transactionManager = "transactionManager", rollbackFor = Exception.class)
    public int reissueOrder(String payOrderId, Long userId, Long realAmount, String remark, byte operateUserBelongSystem) {
        int updateCount = 0;
        PayOrder payOrder = findByPayOrderId(payOrderId);
        ResendRecord resendRecord = null;
        if(payOrder == null) return updateCount;

        // 订单不是初始,支付中,关闭状态
        if(payOrder.getStatus() != PayConstant.PAY_STATUS_INIT && payOrder.getStatus() != PayConstant.PAY_STATUS_PAYING && payOrder.getStatus() != PayConstant.PAY_STATUS_CLOSED) {
            _log.info("当前订单status={},不能补单", payOrder.getStatus());
            return updateCount;
        }

        // 开始补单
        if(payOrder.getStatus() == PayConstant.PAY_STATUS_INIT || payOrder.getStatus() == PayConstant.PAY_STATUS_PAYING) { // 初始或支付中
            //补单时输入了实付金额，且不等于订单实付金额，重新冻结保证金
            if(realAmount != null && !realAmount.equals(payOrder.getAmount())) {
                updateOrderCost(payOrder, realAmount);
            }
            // 存入补单记录
            resendRecord = resendRecordService.insertRecord(payOrderId, userId, remark, operateUserBelongSystem);
            // 修改状态为支付成功
            updateCount = updateStatus4Success(payOrderId);
            _log.info("[补单]userId={},payOrderId={},将支付中修改为支付成功,返回结果:{}", userId, payOrder.getPayOrderId(), updateCount);
        }else if(payOrder.getStatus() == PayConstant.PAY_STATUS_CLOSED) {
            //如果输入了实付金额，且不等于订单实付金额，重新计算订单相关分润
            if (realAmount != null && !realAmount.equals(payOrder.getAmount())) {
                updateOrderCost(payOrder, realAmount);
            }
            // 存入补单记录
            resendRecord = resendRecordService.insertRecord(payOrderId, userId, remark, operateUserBelongSystem);
            // 修改状态为支付成功
            updateCount = updateStatus4Success2(payOrderId);
            _log.info("[补单]userId={},payOrderId={},将关闭修改为支付成功,返回结果:{}", userId, payOrder.getPayOrderId(), updateCount);
        }
        if(updateCount == 0) {
            throw new ServiceException(RetEnum.RET_SERVICE_REISSUE_ERROR); // 补单异常,回滚事务
        }
        // 更新补单任务完成
        resendRecordService.updateState2Finish(resendRecord);

        // 更新订单为已补单
        updateReissue(payOrderId, PayConstant.PAY_ISREISSUE_YES);

        return updateCount;
    }

    @Override
    @Transactional(transactionManager = "transactionManager", rollbackFor = Exception.class)
    public void updateOrderCost(PayOrder payOrder, Long realAmount) {
        OrderCostFeeVO orderCostFeeVO = XXPayUtil.calOrderCostFeeAndIncome(realAmount, payOrder.getChannelRate(), payOrder.getAgentRate(), payOrder.getParentAgentRate(), payOrder.getMchRate(),
                payOrder.getPayPassageRate());

        PayOrder updateRecord = new PayOrder();
        updateRecord.setPayOrderId(payOrder.getPayOrderId());
        updateRecord.setAmount(realAmount);
        updateRecord.setPaymentAmount(realAmount);
        updateRecord.setChannelCost(orderCostFeeVO.getChannelCostFee()); //渠道手续费(配置计算费用)
        updateRecord.setPayPassageCost(orderCostFeeVO.getPayPassageCostFee()); //支付通道设置手续费(中间值)
        updateRecord.setAgentProfit(orderCostFeeVO.getAgentProfit());
        updateRecord.setParentAgentProfit(orderCostFeeVO.getParentAgentProfit());
        updateRecord.setMchIncome(orderCostFeeVO.getMchIncome());
        updateRecord.setPlatProfit(orderCostFeeVO.getPlatProfit());  //平台总利润  商户侧利润
        updateRecord.setPlatMchProfit(orderCostFeeVO.getPlatMchProfit()); //商户侧利润

        int updateCount = payOrderMapper.updateByPrimaryKeySelective(updateRecord);
        if(updateCount < 1) {
            throw new ServiceException(RetEnum.RET_SERVICE_REISSUE_ERROR); // 补单异常,回滚事务
        }
    }

    @Override
    @Transactional(transactionManager = "transactionManager", rollbackFor = Exception.class)
    public int saveTopayUrl(String payOrderId, String topayUrl) {
        PayOrder payOrder = new PayOrder();
        if(StringUtils.isBlank(topayUrl)) return 1;
        payOrder.setTopayUrl(MyBase64.encode(topayUrl.getBytes()));
        PayOrderExample example = new PayOrderExample();
        PayOrderExample.Criteria criteria = example.createCriteria();
        criteria.andPayOrderIdEqualTo(payOrderId);
        return payOrderMapper.updateByExampleSelective(payOrder, example);
    }

    @Override
    public Integer count(PayOrder payOrder) {
        PayOrderExample example = new PayOrderExample();
        PayOrderExample.Criteria criteria = example.createCriteria();
        setCriteria(criteria, payOrder);
        return payOrderMapper.countByExample(example);
    }

    void setCriteria(PayOrderExample.Criteria criteria, PayOrder payOrder, Date createTimeStart, Date createTimeEnd) {
        if(payOrder != null) {
            if(payOrder.getMchId() != null) criteria.andMchIdEqualTo(payOrder.getMchId());
            if(payOrder.getPassageAccountId() != null) criteria.andPassageAccountIdEqualTo(payOrder.getPassageAccountId());
            if(payOrder.getPassageId() != null) criteria.andPassageIdEqualTo(payOrder.getPassageId());
            if(payOrder.getProductId() != null && payOrder.getProductId() != -99) criteria.andProductIdEqualTo(payOrder.getProductId());
            if((payOrder.getAgentId() != null)) criteria.andAgentIdEqualTo(payOrder.getAgentId());
            if(StringUtils.isNotBlank(payOrder.getPayOrderId())) criteria.andPayOrderIdEqualTo(payOrder.getPayOrderId());
            if(StringUtils.isNotBlank(payOrder.getMchOrderNo())) criteria.andMchOrderNoEqualTo(payOrder.getMchOrderNo());
            if(StringUtils.isNotBlank(payOrder.getChannelOrderNo())) criteria.andChannelOrderNoEqualTo(payOrder.getChannelOrderNo());
            if(payOrder.getStatus() != null && payOrder.getStatus() != -99) criteria.andStatusEqualTo(payOrder.getStatus());
            if(payOrder.getProductType() != null && payOrder.getProductType() != -99) criteria.andProductTypeEqualTo(payOrder.getProductType());
            if(StringUtils.isNotBlank(payOrder.getChannelMchId())) criteria.andChannelMchIdEqualTo(payOrder.getChannelMchId());
            if(payOrder.getIsReissue() != null && payOrder.getIsReissue() != -99) criteria.andIsReissueEqualTo(payOrder.getIsReissue());
            if(StringUtils.isNotEmpty(payOrder.getSubject())) criteria.andSubjectEqualTo(payOrder.getSubject());
            if(payOrder.getAmount() != null) criteria.andAmountEqualTo(payOrder.getAmount());
            if (StringUtils.isNotEmpty(payOrder.getSkBank())) criteria.andSkBankEqualTo(payOrder.getSkBank());
            if (StringUtils.isNotEmpty(payOrder.getSkName())) criteria.andSkNameEqualTo(payOrder.getSkName());

            if(payOrder.getPs() != null){
                //创建时间 >= xx (字符串类型， 标准日期时间格式)
                if(StringUtils.isNotEmpty(payOrder.getPsStringVal("createdAtGeStr"))){
                    criteria.andCreateTimeGreaterThanOrEqualTo(DateUtil.str2date(payOrder.getPsStringVal("createdAtGeStr")));
                }
            }

        }
        if(createTimeStart != null) {
            criteria.andCreateTimeGreaterThanOrEqualTo(createTimeStart);
        }
        if(createTimeEnd != null) {
            criteria.andCreateTimeLessThanOrEqualTo(createTimeEnd);
        }
    }

    void setCriteria(PayOrderExample.Criteria criteria, PayOrder payOrder) {
        setCriteria(criteria, payOrder, null, null);
    }



}
